<!DOCTYPE html>
<meta charset="UTF-8">
<body>
<script src="../interpolation/resources/interpolation-test.js"></script>
<script>
// Basic additive composition; the lists should be concatenated.
assertComposition({
  property: 'backdrop-filter',
  underlying: 'blur(10px)',
  addFrom: 'blur(40px)',
  addTo: 'blur(90px)',
}, [
  {at: -0.5, is: 'blur(10px) blur(15px)'},
  {at: 0, is: 'blur(10px) blur(40px)'},
  {at: 0.25, is: 'blur(10px) blur(52.5px)'},
  {at: 0.5, is: 'blur(10px) blur(65px)'},
  {at: 0.75, is: 'blur(10px) blur(77.5px)'},
  {at: 1, is: 'blur(10px) blur(90px)'},
  {at: 1.5, is: 'blur(10px) blur(115px)'},
]);

// Here we have add-from and replace-to, so the list will be have mismatched
// lengths and the replace-to list will have to be extended to interpolate as
// per https://drafts.fxtf.org/filter-effects-1/#interpolation-of-filters
//
// That is, this becomes an interpolation of the form:
//   sepia(0.5) sepia(0.5) --> sepia(1) sepia(0)
assertComposition({
  property: 'backdrop-filter',
  underlying: 'sepia(0.5)',
  addFrom: 'sepia(0.5)',
  replaceTo: 'sepia(1)',
}, [
  {at: -0.5, is: 'sepia(0.25) sepia(0.75)'},
  {at: 0, is: 'sepia(0.5) sepia(0.5)'},
  {at: 0.25, is: 'sepia(0.625) sepia(0.375)'},
  {at: 0.5, is: 'sepia(0.75) sepia(0.25)'},
  {at: 0.75, is: 'sepia(0.875) sepia(0.125)'},
  {at: 1, is: 'sepia(1) sepia(0)'},
  {at: 1.5, is: 'sepia(1) sepia(0)'},
]);

// In this case we have replace-from and add-to, so similar extending behavior
// takes place. Note that brightness has an initial value of 1.
//
// That is, this becomes an interpolation of the form:
//   brightness(0.5) brightness(1) --> brightness(0) brightness(1.5)
assertComposition({
  property: 'backdrop-filter',
  underlying: 'brightness(0)',
  replaceFrom: 'brightness(0.5)',
  addTo: 'brightness(1.5)',
}, [
  {at: -0.5, is: 'brightness(0.75) brightness(0.75)'},
  {at: 0, is: 'brightness(0.5) brightness(1)'},
  {at: 0.25, is: 'brightness(0.375) brightness(1.125)'},
  {at: 0.5, is: 'brightness(0.25) brightness(1.25)'},
  {at: 0.75, is: 'brightness(0.125) brightness(1.375)'},
  {at: 1, is: 'brightness(0) brightness(1.5)'},
  {at: 1.5, is: 'brightness(0) brightness(1.75)'},
]);

// Test mixing properties.
assertComposition({
  property: 'backdrop-filter',
  underlying: 'invert(0.5)',
  addFrom: 'saturate(2)',
  addTo: 'saturate(3)',
}, [
  {at: -0.5, is: 'invert(0.5) saturate(1.5)'},
  {at: 0, is: 'invert(0.5) saturate(2)'},
  {at: 0.25, is: 'invert(0.5) saturate(2.25)'},
  {at: 0.5, is: 'invert(0.5) saturate(2.5)'},
  {at: 0.75, is: 'invert(0.5) saturate(2.75)'},
  {at: 1, is: 'invert(0.5) saturate(3)'},
  {at: 1.5, is: 'invert(0.5) saturate(3.5)'},
]);

// Test the 'none' behavior; composition happens before interpolation, so this
// is actually an interpolation of:
//   invert(0.5) saturate(1) --> invert(1) saturate(3)
assertComposition({
  property: 'backdrop-filter',
  underlying: 'invert(0.5)',
  addFrom: 'none',
  replaceTo: 'invert(1) saturate(3)',
}, [
  {at: -0.5, is: 'invert(0.25) saturate(0)'},
  {at: 0, is: 'invert(0.5) saturate(1)'},
  {at: 0.25, is: 'invert(0.625) saturate(1.5)'},
  {at: 0.5, is: 'invert(0.75) saturate(2)'},
  {at: 0.75, is: 'invert(0.875) saturate(2.5)'},
  {at: 1, is: 'invert(1) saturate(3)'},
  {at: 1.5, is: 'invert(1.25) saturate(4)'},
]);

// Test having multiple underlying values
assertComposition({
  property: 'backdrop-filter',
  underlying: 'grayscale(25%) blur(10px)',
  addFrom: 'brightness(0)',
  addTo: 'brightness(1)',
}, [
  {at: -0.5, is: 'grayscale(25%) blur(10px) brightness(0)'},
  {at: 0, is: 'grayscale(25%) blur(10px) brightness(0)'},
  {at: 0.25, is: 'grayscale(25%) blur(10px) brightness(0.25)'},
  {at: 0.5, is: 'grayscale(25%) blur(10px) brightness(0.5)'},
  {at: 0.75, is: 'grayscale(25%) blur(10px) brightness(0.75)'},
  {at: 1, is: 'grayscale(25%) blur(10px) brightness(1)'},
  {at: 1.5, is: 'grayscale(25%) blur(10px) brightness(1.5)'},
]);

// Make sure that a matching underlying value is still prefixed.
assertComposition({
  property: 'backdrop-filter',
  underlying: 'blur(10px)',
  addFrom: 'grayscale(50%) blur(10px)',
  addTo: 'grayscale(25%) blur(10px)',
}, [
  {at: -0.5, is: 'blur(10px) grayscale(0.625) blur(10px)'},
  {at: 0, is: 'blur(10px) grayscale(0.5) blur(10px)'},
  {at: 0.25, is: 'blur(10px) grayscale(0.4375) blur(10px)'},
  {at: 0.5, is: 'blur(10px) grayscale(0.375) blur(10px)'},
  {at: 0.75, is: 'blur(10px) grayscale(0.3125) blur(10px)'},
  {at: 1, is: 'blur(10px) grayscale(0.25) blur(10px)'},
  {at: 1.5, is: 'blur(10px) grayscale(0.125) blur(10px)'},
]);

// Check the case where composition causes a url() to be included; the animation
// should change to discrete.
assertComposition({
  property: 'backdrop-filter',
  underlying: 'url(#a)',
  addFrom: 'grayscale(50%) blur(30px)',
  addTo: 'grayscale(25%) blur(40px)',
}, [
  {at: -0.5, is: 'url("#a") grayscale(0.5) blur(30px)'},
  {at: 0, is: 'url("#a") grayscale(0.5) blur(30px)'},
  {at: 0.25, is: 'url("#a") grayscale(0.5) blur(30px)'},
  {at: 0.5, is: 'url("#a") grayscale(0.25) blur(40px)'},
  {at: 0.75, is: 'url("#a") grayscale(0.25) blur(40px)'},
  {at: 1, is: 'url("#a") grayscale(0.25) blur(40px)'},
  {at: 1.5, is: 'url("#a") grayscale(0.25) blur(40px)'},
]);

// And check the inverse; nothing fancy here but it should be a discrete
// animation with blur prepended.
assertComposition({
  property: 'backdrop-filter',
  underlying: 'blur(10px)',
  addFrom: 'url(#a) brightness(1)',
  addTo: 'url(#b) brightness(0)',
}, [
  {at: -0.5, is: 'blur(10px) url(#a) brightness(1)'},
  {at: 0, is: 'blur(10px) url(#a) brightness(1)'},
  {at: 0.25, is: 'blur(10px) url(#a) brightness(1)'},
  {at: 0.5, is: 'blur(10px) url(#b) brightness(0)'},
  {at: 0.75, is: 'blur(10px) url(#b) brightness(0)'},
  {at: 1, is: 'blur(10px) url(#b) brightness(0)'},
  {at: 1.5, is: 'blur(10px) url(#b) brightness(0)'},
]);

// --------------- Accumulation tests. ---------------------

// blur; simple addition.
assertComposition({
  property: 'backdrop-filter',
  underlying: 'blur(10px)',
  accumulateFrom: 'blur(40px)',
  accumulateTo: 'blur(90px)',
}, [
  {at: -0.5, is: 'blur(25px)'},
  {at: 0, is: 'blur(50px)'},
  {at: 0.25, is: 'blur(62.5px)'},
  {at: 0.5, is: 'blur(75px)'},
  {at: 0.75, is: 'blur(87.5px)'},
  {at: 1, is: 'blur(100px)'},
  {at: 1.5, is: 'blur(125px)'},
]);

// brightness; 1-based addition.
assertComposition({
  property: 'backdrop-filter',
  underlying: 'brightness(0.25)',
  accumulateFrom: 'brightness(0.5)',
  accumulateTo: 'brightness(1.5)',
}, [
  {at: -0.5, is: 'brightness(0)'},
  {at: 0, is: 'brightness(0)'},
  {at: 0.25, is: 'brightness(0)'},
  {at: 0.5, is: 'brightness(0.25)'},
  {at: 0.75, is: 'brightness(0.5)'},
  {at: 1, is: 'brightness(0.75)'},
  {at: 1.5, is: 'brightness(1.25)'},
]);

// contrast; 1-based addition.
assertComposition({
  property: 'backdrop-filter',
  underlying: 'contrast(0.25)',
  accumulateFrom: 'contrast(0.5)',
  accumulateTo: 'contrast(1.5)',
}, [
  {at: -0.5, is: 'contrast(0)'},
  {at: 0, is: 'contrast(0)'},
  {at: 0.25, is: 'contrast(0)'},
  {at: 0.5, is: 'contrast(0.25)'},
  {at: 0.75, is: 'contrast(0.5)'},
  {at: 1, is: 'contrast(0.75)'},
  {at: 1.5, is: 'contrast(1.25)'},
]);

// drop-shadow; addition of lengths plus color addition
assertComposition({
  property: 'backdrop-filter',
  underlying: 'drop-shadow(10px 5px 0px rgb(255, 0, 0))',
  accumulateFrom: 'drop-shadow(0px 10px 10px rgb(0, 255, 0))',
  accumulateTo: 'drop-shadow(50px 30px 10px rgb(0, 0, 255))',
}, [
  {at: -0.5, is: 'drop-shadow(-15px 5px 10px rgb(255, 255, 0))'},
  {at: 0, is: 'drop-shadow(10px 15px 10px rgb(255, 255, 0))'},
  {at: 0.25, is: 'drop-shadow(22.5px 20px 10px rgb(255, 191, 64))'},
  {at: 0.5, is: 'drop-shadow(35px 25px 10px rgb(255, 128, 128))'},
  {at: 0.75, is: 'drop-shadow(47.5px 30px 10px rgb(255, 64, 191))'},
  {at: 1, is: 'drop-shadow(60px 35px 10px rgb(255, 0, 255))'},
  {at: 1.5, is: 'drop-shadow(85px 45px 10px rgb(255, 0, 255))'},
]);

// grayscale; 1-based addition.
assertComposition({
  property: 'backdrop-filter',
  underlying: 'grayscale(0.25)',
  accumulateFrom: 'grayscale(0.5)',
  accumulateTo: 'grayscale(1.5)',  // clamped to 1
}, [
  {at: -0.5, is: 'grayscale(0)'},
  {at: 0, is: 'grayscale(0)'},
  {at: 0.25, is: 'grayscale(0)'},
  {at: 0.5, is: 'grayscale(0)'},
  {at: 0.75, is: 'grayscale(0.125)'},
  {at: 1, is: 'grayscale(0.25)'},
  {at: 1.5, is: 'grayscale(0.5)'},
]);

// hue-rotate; simple addition
assertComposition({
  property: 'backdrop-filter',
  underlying: 'hue-rotate(45deg)',
  accumulateFrom: 'hue-rotate(140deg)',
  accumulateTo: 'hue-rotate(400deg)',
}, [
  {at: -0.5, is: 'hue-rotate(55deg)'},
  {at: 0, is: 'hue-rotate(185deg)'},
  {at: 0.25, is: 'hue-rotate(250deg)'},
  {at: 0.5, is: 'hue-rotate(315deg)'},
  {at: 0.75, is: 'hue-rotate(380deg)'},
  {at: 1, is: 'hue-rotate(445deg)'},
  {at: 1.5, is: 'hue-rotate(575deg)'},
]);

// invert; 1-based addition.
assertComposition({
  property: 'backdrop-filter',
  underlying: 'invert(0.25)',
  accumulateFrom: 'invert(0.5)',
  accumulateTo: 'invert(1.5)',  // clamped to 1
}, [
  {at: -0.5, is: 'invert(0)'},
  {at: 0, is: 'invert(0)'},
  {at: 0.25, is: 'invert(0)'},
  {at: 0.5, is: 'invert(0)'},
  {at: 0.75, is: 'invert(0.125)'},
  {at: 1, is: 'invert(0.25)'},
  {at: 1.5, is: 'invert(0.5)'},
]);

// opacity; 1-based addition
assertComposition({
  property: 'backdrop-filter',
  underlying: 'opacity(0.25)',
  accumulateFrom: 'opacity(0.5)',
  accumulateTo: 'opacity(1.5)',  // clamped to 1
}, [
  {at: -0.5, is: 'opacity(0)'},
  {at: 0, is: 'opacity(0)'},
  {at: 0.25, is: 'opacity(0)'},
  {at: 0.5, is: 'opacity(0)'},
  {at: 0.75, is: 'opacity(0.125)'},
  {at: 1, is: 'opacity(0.25)'},
  {at: 1.5, is: 'opacity(0.5)'},
]);

// saturate; 1-based addition
assertComposition({
  property: 'backdrop-filter',
  underlying: 'saturate(0.25)',
  accumulateFrom: 'saturate(0.5)',
  accumulateTo: 'saturate(1.5)',
}, [
  {at: -0.5, is: 'saturate(0)'},
  {at: 0, is: 'saturate(0)'},
  {at: 0.25, is: 'saturate(0)'},
  {at: 0.5, is: 'saturate(0.25)'},
  {at: 0.75, is: 'saturate(0.5)'},
  {at: 1, is: 'saturate(0.75)'},
  {at: 1.5, is: 'saturate(1.25)'},
]);

// sepia; 1-based addition
assertComposition({
  property: 'backdrop-filter',
  underlying: 'sepia(0.25)',
  accumulateFrom: 'sepia(0.5)',
  accumulateTo: 'sepia(1.5)',  // clamped to 1
}, [
  {at: -0.5, is: 'sepia(0)'},
  {at: 0, is: 'sepia(0)'},
  {at: 0.25, is: 'sepia(0)'},
  {at: 0.5, is: 'sepia(0)'},
  {at: 0.75, is: 'sepia(0.125)'},
  {at: 1, is: 'sepia(0.25)'},
  {at: 1.5, is: 'sepia(0.5)'},
]);

// url; cannot be accumulated
assertComposition({
  property: 'backdrop-filter',
  underlying: 'url(#a)',
  accumulateFrom: 'url(#b)',
  accumulateTo: 'url(#c)',
}, [
  {at: -0.5, is: 'url(#b)'},
  {at: 0, is: 'url(#b)'},
  {at: 0.25, is: 'url(#b)'},
  {at: 0.5, is: 'url(#c)'},
  {at: 0.75, is: 'url(#c)'},
  {at: 1, is: 'url(#c)'},
  {at: 1.5, is: 'url(#c)'},
]);

// Test auto-extension of the underlying list.
assertComposition({
  property: 'backdrop-filter',
  underlying: 'blur(10px)',
  accumulateFrom: 'blur(40px) saturate(1)',
  accumulateTo: 'blur(90px) saturate(0)',
}, [
  {at: -0.5, is: 'blur(25px) saturate(1.5)'},
  {at: 0, is: 'blur(50px) saturate(1)'},
  {at: 0.25, is: 'blur(62.5px) saturate(0.75)'},
  {at: 0.5, is: 'blur(75px) saturate(0.5)'},
  {at: 0.75, is: 'blur(87.5px) saturate(0.25)'},
  {at: 1, is: 'blur(100px) saturate(0)'},
  {at: 1.5, is: 'blur(125px) saturate(0)'},
]);

// Test auto-extension of the composited-onto list.
assertComposition({
  property: 'backdrop-filter',
  underlying: 'blur(10px) saturate(0.75)',
  accumulateFrom: 'blur(40px)',
  accumulateTo: 'blur(90px)',
}, [
  {at: -0.5, is: 'blur(25px) saturate(0.75)'},
  {at: 0, is: 'blur(50px) saturate(0.75)'},
  {at: 0.25, is: 'blur(62.5px) saturate(0.75)'},
  {at: 0.5, is: 'blur(75px) saturate(0.75)'},
  {at: 0.75, is: 'blur(87.5px) saturate(0.75)'},
  {at: 1, is: 'blur(100px) saturate(0.75)'},
  {at: 1.5, is: 'blur(125px) saturate(0.75)'},
]);

// Mismatching type for underlying; it just gets replaced.
assertComposition({
  property: 'backdrop-filter',
  underlying: 'contrast(0.75)',
  accumulateFrom: 'blur(40px)',
  accumulateTo: 'blur(80px)',
}, [
  {at: -0.5, is: 'blur(20px)'},
  {at: 0, is: 'blur(40px)'},
  {at: 0.25, is: 'blur(50px)'},
  {at: 0.5, is: 'blur(60px)'},
  {at: 0.75, is: 'blur(70px)'},
  {at: 1, is: 'blur(80px)'},
  {at: 1.5, is: 'blur(100px)'},
]);

// Underlying only type-matches one side of the interpolation; it should be
// accumulated onto that side, but the entire animation will be discrete due to
// the mis-matching types.
assertComposition({
  property: 'backdrop-filter',
  underlying: 'blur(10px)',
  accumulateFrom: 'blur(40px)',
  accumulateTo: 'saturate(1)',
}, [
  {at: -0.5, is: 'blur(50px)'},
  {at: 0, is: 'blur(50px)'},
  {at: 0.25, is: 'blur(50px)'},
  {at: 0.5, is: 'saturate(1)'},
  {at: 0.75, is: 'saturate(1)'},
  {at: 1, is: 'saturate(1)'},
  {at: 1.5, is: 'saturate(1)'},
]);

// Test a case where only one side is accumulative and the other is replace.
assertComposition({
  property: 'backdrop-filter',
  underlying: 'blur(10px)',
  accumulateFrom: 'blur(30px)',
  replaceTo: 'blur(100px)',
}, [
  {at: -0.5, is: 'blur(10px)'},
  {at: 0, is: 'blur(40px)'},
  {at: 0.25, is: 'blur(55px)'},
  {at: 0.5, is: 'blur(70px)'},
  {at: 0.75, is: 'blur(85px)'},
  {at: 1, is: 'blur(100px)'},
  {at: 1.5, is: 'blur(130px)'},
]);

// Test a case where only one side is accumulative and the other is add.
// This basically looks like:
//   accumulateSide = blur(Apx) neutral-blur
//   addSide        = blur(10px) blur(Bpx)
assertComposition({
  property: 'backdrop-filter',
  underlying: 'blur(10px)',
  accumulateFrom: 'blur(40px)',
  addTo: 'blur(100px)',
}, [
  {at: -0.5, is: 'blur(70px) blur(0px)'},
  {at: 0, is: 'blur(50px) blur(0px)'},
  {at: 0.25, is: 'blur(40px) blur(25px)'},
  {at: 0.5, is: 'blur(30px) blur(50px)'},
  {at: 0.75, is: 'blur(20px) blur(75px)'},
  {at: 1, is: 'blur(10px) blur(100px)'},
  {at: 1.5, is: 'blur(0px) blur(150px)'},
]);
</script>
</body>
